package com.gitee.luosl.cola.lang;

import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;

public final class Tuple2<T1, T2> implements Tuple {

    public final T1 _1;

    public final T2 _2;

    Tuple2(T1 left, T2 right) {
        this._1 = left;
        this._2 = right;
    }

    public T1 get_1() {
        return _1;
    }

    public T2 get_2() {
        return _2;
    }

    public <R> Tuple2<R, T2> map1(Function<? super T1, ? extends R> function){
        return Tuple.of(function.apply(_1), _2);
    }

    public <R> Tuple2<T1, R> map2(Function<? super T2, ? extends R> function){
        return Tuple.of(_1, function.apply(_2));
    }

    public <R1, R2> Tuple2<R1, R2> map(BiFunction<? super T1, ? super T2, Tuple2<R1, R2>> mapper){
        return apply(mapper);
    }

    public <R1, R2> Tuple2<R1, R2> map(Function<T1, R1> mapper1, Function<T2, R2> mapper2){
        return map1(mapper1).map2(mapper2);
    }

    public <R> R apply(BiFunction<? super T1, ? super T2, ? extends R> function) {
        return function.apply(_1, _2);
    }

    public void accept(BiConsumer<? super T1, ? super T2> consumer) {
        consumer.accept(_1, _2);
    }

    public boolean test(BiPredicate<? super T1, ? super T2> predicate){
        return predicate.test(_1, _2);
    }

    public Tuple2<T2, T1> reverse(){
        return Tuple.of(_2, _1);
    }

    @Override
    public int hashCode() {
        int result = 1;
        result = 31 * result + Objects.hashCode(_1);
        result = 31 * result + Objects.hashCode(_2);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (!(obj instanceof Tuple2)) {
            return false;
        } else {
            final Tuple2<?, ?> that = (Tuple2<?, ?>) obj;
            return Objects.equals(this._1, that._1)
                    && Objects.equals(this._2, that._2);
        }
    }

    @Override
    public String toString() {
        return String.format(
                "(%s, %s)",
                Objects.toString(_1),
                Objects.toString(_2)
        );
    }
}
